/*
 * Copyright (C) 2025 Tsingteng MicroSystem
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "nfc_osi_api.h"
#include "nfc_log_api.h"
#include <string.h>
#include <errno.h>
#include <time.h>
#include <inttypes.h>

/**
 * @file osi_mutex.c
 * @brief 互斥锁相关的API实现
*/
#ifdef OSI_MUTEX_DEBUG
#define MUTEX_TIMEOUT_60S (60)
#endif

static inline tNFC_STATUS check_mutex_state(tMUTEX *mutex)
{
    if (mutex == NULL) {
        NFCLOG_E("mutex is NULL");
        return NFC_STATUS_INVALID_PARAM;
    }
    uint32_t current_state = osi_atomic_read(&mutex->state);
    if (!(current_state == MUTEX_STATE_INITED || current_state == MUTEX_STATE_STATIC)) {
        NFCLOG_E("[%s] mutex is not inited", mutex->name);
        return NFC_STATUS_NOT_INIT;
    }
    return NFC_STATUS_OK;
}

/**
 * @brief 初始化互斥锁
 *
 * @param mutex 互斥锁指针
 * @return 成功返回NFC_STATUS_OK，其他情况返回对应错误码
*/
tNFC_STATUS osi_mutex_init(tMUTEX *mutex)
{
    return osi_mutex_init_with_attr(mutex, false);
}

static tNFC_STATUS check_mutex_init_state(tMUTEX *mutex)
{
    if (NULL == mutex) {
        NFCLOG_E("mutex is NULL");
        return NFC_STATUS_INVALID_PARAM;
    }
    uint32_t mutex_old_state = osi_atomic_read(&mutex->state);
    if (MUTEX_STATE_INITED == mutex_old_state) {
        NFCLOG_W("[%s] mutex is already inited", mutex->name);
        return NFC_STATUS_OK;
    } else if (MUTEX_STATE_NOT_INIT != mutex_old_state) {
        // 只允许not init状态的mutex进行初始化操作
        NFCLOG_W("[%s] mutex init racing. state:%" PRIu32, mutex->name, mutex_old_state);
        return NFC_STATUS_REFUSED;
    }
    if (MUTEX_STATE_NOT_INIT != osi_atomic_read_then_write(&mutex->state, MUTEX_STATE_INITIALIZING)) {
        // 再次确认是否有init流程在并发
        NFCLOG_W("[%s] mutex init racing.", mutex->name);
        return NFC_STATUS_REFUSED;
    }
    return NFC_STATUS_OK;
}

/**
 * @brief 带参数初始化互斥锁
 *
 * @param mutex 互斥锁指针
 * @param is_recursive 是否是可重入锁，true：同一个线程可以多次加锁，false：同一个线程只能加锁一次
 * @return 成功返回NFC_STATUS_OK，其他情况返回对应错误码
*/
tNFC_STATUS osi_mutex_init_with_attr(tMUTEX *mutex, bool is_recursive)
{
    tNFC_STATUS status = check_mutex_init_state(mutex);
    if (status != NFC_STATUS_OK) {
        return status;
    }
    // tMUTEX中含有原子变量，一般不对原子变量做memset操作，所以需要手动对每个成员进行初始化
    pthread_mutexattr_t mutexattr;
    int err = pthread_mutexattr_init(&mutexattr);
    if (err != 0) {
        NFCLOG_E("[%s] pthread_mutexattr_init failed, ret=%d", mutex->name, err);
        return NFC_STATUS_FAILED;
    }
    if (is_recursive) {
        err = pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);
        if (err != 0) {
            NFCLOG_E("[%s] pthread_mutexattr_settype failed, ret=%d", mutex->name, err);
            goto cleanup_mutexattr;
        }
    }

    err = pthread_mutex_init(&mutex->mutex, &mutexattr);
    if (err != 0) {
        NFCLOG_E("[%s] pthread_mutex_init failed, err=%d", mutex->name, err);
        return NFC_STATUS_FAILED;
    }
    pthread_condattr_t attr;
    if (pthread_condattr_init(&attr) != 0) {
        NFCLOG_E("[%s] pthread_condattr_init failed", mutex->name);
        goto cleanup_mutex;
    }
    if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) != 0) {
        NFCLOG_E("[%s] pthread_condattr_setclock failed", mutex->name);
        goto cleanup_condattr;
    }
    if (pthread_cond_init(&mutex->cond, &attr) != 0) {
        NFCLOG_E("[%s] pthread_cond_init failed", mutex->name);
        goto cleanup_condattr;
    }
    pthread_mutexattr_destroy(&mutexattr);
    pthread_condattr_destroy(&attr);
    mutex->is_recursive = is_recursive;
    osi_atomic_write(&mutex->state, MUTEX_STATE_INITED);
    return NFC_STATUS_OK;
cleanup_condattr:
    pthread_condattr_destroy(&attr);
cleanup_mutex:
    pthread_mutex_destroy(&mutex->mutex);
cleanup_mutexattr:
    pthread_mutexattr_destroy(&mutexattr);
    return NFC_STATUS_FAILED;
}

/**
 * @brief 销毁互斥锁
 *
 * 销毁互斥锁，释放资源
 * @note 调用本函数前，确保所有的锁都已经释放
 *
 * @param mutex 互斥锁指针
 * @return 成功返回NFC_STATUS_OK，其他情况返回对应错误码
 *
*/
tNFC_STATUS osi_mutex_deinit(tMUTEX *mutex)
{
    tNFC_STATUS ret = check_mutex_state(mutex);
    if (ret != NFC_STATUS_OK) {
        return ret;
    }
    OSI_CHK_PARAM_INVALID(NULL == mutex->name, return NFC_STATUS_INVALID_PARAM);
#ifndef NO_MUTEX_DESTROY
    int err;
    if (MUTEX_STATE_STATIC == osi_atomic_read(&mutex->state)) {
        NFCLOG_W("[%s] mutex is static, not deinit", mutex->name);
        return NFC_STATUS_REFUSED;
    }
    if (MUTEX_STATE_INITED != osi_atomic_read_then_write(&mutex->state, MUTEX_STATE_DESTROYING)) {
        NFCLOG_W("[%s] mutex deinit racing", mutex->name);
        return NFC_STATUS_REFUSED;
    }
    err = pthread_mutex_destroy(&mutex->mutex);
    if (err != 0) {
        NFCLOG_E("[%s] pthread_mutex_destroy failed, err=%d", mutex->name, err);
        ret = NFC_STATUS_FAILED;
    }
    err = pthread_cond_destroy(&mutex->cond);
    if (err != 0) {
        NFCLOG_E("[%s] pthread_cond_destroy failed, err=%d", mutex->name, err);
        ret = NFC_STATUS_FAILED;
    }
    osi_atomic_write(&mutex->state, MUTEX_STATE_NOT_INIT);
#endif
    return ret;
}

/**
 * @brief 获取互斥锁
 *
 * 获取互斥锁，如果互斥锁已经被其他线程获取，则当前线程进入阻塞状态
 *
 * @param mutex 互斥锁指针
 * @return 成功返回NFC_STATUS_OK，其他情况返回对应错误码
*/
tNFC_STATUS osi_mutex_lock(tMUTEX *mutex)
{
    tNFC_STATUS ret = check_mutex_state(mutex);
    if (ret != NFC_STATUS_OK) {
        return ret;
    }
    int err;
#ifdef OSI_MUTEX_DEBUG
    struct timespec timeout;
    while (true) {
        clock_gettime(CLOCK_REALTIME, &timeout);
        timeout.tv_sec += MUTEX_TIMEOUT_60S;
        err = pthread_mutex_timedlock(&mutex->mutex, &timeout);
        if (err == 0) {
            break;
        } else if (err == ETIMEDOUT) {
            NFCLOG_W("[%s] timeout acquiring, retry...", mutex->name);
        } else {
            NFCLOG_E("[%s] pthread_mutex_timedlock failed, err=%d", mutex->name, err);
            break;
        }
    }
#else
    err = pthread_mutex_lock(&mutex->mutex);
#endif
    if (err == 0) {
        ret = NFC_STATUS_OK;
    } else {
        ret = NFC_STATUS_FAILED;
    }
    return ret;
}

/**
 * @brief 尝试获取互斥锁
 *
 * 尝试获取互斥锁，如果互斥锁已经被其他线程获取，则当前线程不会进入阻塞状态
 *
 * @param 互斥锁指针
 * @return 成功获取锁返回NFC_STATUS_OK，其他情况返回对应错误码
*/
tNFC_STATUS osi_mutex_trylock(tMUTEX *mutex)
{
    tNFC_STATUS ret = check_mutex_state(mutex);
    if (ret != NFC_STATUS_OK) {
        return ret;
    }
    int err;
    err = pthread_mutex_trylock(&mutex->mutex);
    if (err == 0) {
        ret = NFC_STATUS_OK;
    } else {
        ret = NFC_STATUS_FAILED;
    }
    return ret;
}

/**
 * @brief 获取互斥锁，超时还获取不到锁返回错误
 * @param mutex 互斥锁指针
 * @param timeout_ms 超时时间，单位毫秒
 * @return 成功返回NFC_STATUS_OK
 *         超时返回NFC_STATUS_TIMEOUT
 *         其他情况返回对应错误码
 */
tNFC_STATUS osi_mutex_lock_timeout(tMUTEX *mutex, uint64_t timeout_ms)
{
    tNFC_STATUS ret = check_mutex_state(mutex);
    if (ret != NFC_STATUS_OK) {
        return ret;
    }
    int err;
    struct timespec timeout_time;
#ifdef OSI_MUTEX_DEBUG
    while (timeout_ms > 0) {
        clock_gettime(CLOCK_REALTIME, &timeout_time);
        if (timeout_ms > (MUTEX_TIMEOUT_60S * OSI_MS_PER_SEC)) {
            timeout_time.tv_sec += MUTEX_TIMEOUT_60S;
            timeout_ms -= MUTEX_TIMEOUT_60S * OSI_MS_PER_SEC;
        } else {
            timeout_time.tv_sec += timeout_ms / OSI_MS_PER_SEC;
            timeout_time.tv_nsec += timeout_ms % OSI_MS_PER_SEC * OSI_NS_PER_MS;
            if (timeout_time.tv_nsec > OSI_NS_PER_SEC) {
                timeout_time.tv_sec++;
                timeout_time.tv_nsec -= OSI_NS_PER_SEC;
            }
            timeout_ms = 0;
        }
        err = pthread_mutex_timedlock(&mutex->mutex, &timeout_time);
        if (err == 0) {
            break;
        } else if (err == ETIMEDOUT) {
            NFCLOG_W("[%s] timeout acquiring, retry...", mutex->name);
        } else {
            NFCLOG_E("[%s] pthread_mutex_timedlock failed, err=%d", mutex->name, err);
            break;
        }
    }
#else
    clock_gettime(CLOCK_REALTIME, &timeout_time);
    timeout_time.tv_sec += timeout_ms / OSI_MS_PER_SEC;
    timeout_time.tv_nsec += timeout_ms % OSI_MS_PER_SEC * OSI_NS_PER_MS;
    if (timeout_time.tv_nsec > OSI_NS_PER_SEC) {
        timeout_time.tv_sec++;
        timeout_time.tv_nsec -= OSI_NS_PER_SEC;
    }
    err = pthread_mutex_timedlock(&mutex->mutex, &timeout_time);
#endif
    if (err == 0) {
        return NFC_STATUS_OK;
    } else if (err == ETIMEDOUT) {
        return NFC_STATUS_TIMEOUT;
    } else {
        return NFC_STATUS_FAILED;
    }
}

/**
 * @brief 释放互斥锁
 *
 * 释放互斥锁
 *
 * @param 互斥锁指针
 * @return 成功返回NFC_STATUS_OK，其他情况返回对应错误码
*/
tNFC_STATUS osi_mutex_unlock(tMUTEX *mutex)
{
    tNFC_STATUS ret = check_mutex_state(mutex);
    if (ret != NFC_STATUS_OK) {
        return ret;
    }
    int err;
    err = pthread_mutex_unlock(&mutex->mutex);
    if (err == 0) {
        ret = NFC_STATUS_OK;
    } else {
        ret = NFC_STATUS_FAILED;
    }
    return ret;
}

/**
 * @brief 等待事件，直到被唤醒
 *
 * 调用后线程进入阻塞状态，等待其他线程调用osi_mutex_notify或是osi_mutex_notify_all唤醒
 *
 * @note 如果无线程调用osi_mutex_notify或是osi_mutex_notify_all唤醒，则线程一直处于阻塞状态
 * @note 调用此函数前需要先获取互斥锁
 *
 * @param mutex 互斥锁指针
 * @return 成功返回NFC_STATUS_OK，其他情况返回对应错误码
*/
tNFC_STATUS osi_mutex_wait_forever(tMUTEX *mutex)
{
    tNFC_STATUS ret = check_mutex_state(mutex);
    if (ret != NFC_STATUS_OK) {
        return ret;
    }
    int err;
#ifdef OSI_MUTEX_DEBUG
    struct timespec timeout;
    while (true) {
        clock_gettime(CLOCK_REALTIME, &timeout);
        timeout.tv_sec += MUTEX_TIMEOUT_60S;
        err = pthread_cond_timedwait(&mutex->cond, &mutex->mutex, &timeout);
        if (err == 0) {
            break;
        } else if (err == ETIMEDOUT) {
            NFCLOG_W("[%s] timeout acquiring, retry...", mutex->name);
        } else {
            NFCLOG_E("[%s] pthread_mutex_timedlock failed, err=%d", mutex->name, err);
            break;
        }
    }
#else
    err = pthread_cond_wait(&mutex->cond, &mutex->mutex);
#endif
    return err == 0 ? NFC_STATUS_OK : NFC_STATUS_FAILED;
}

/**
 * @brief 等待事件，直到被唤醒，无警告信息
 *
 * 调用后线程进入阻塞状态，等待其他线程调用osi_mutex_notify或是osi_mutex_notify_all唤醒
 *
 * @note 如果无线程调用osi_mutex_notify或是osi_mutex_notify_all唤醒，则线程一直处于阻塞状态
 * @note 调用此函数前需要先获取互斥锁
 *
 * @param mutex 互斥锁指针
 * @return 成功返回NFC_STATUS_OK，其他情况返回对应错误码
 */
tNFC_STATUS osi_mutex_wait_forever_no_warn(tMUTEX *mutex)
{
    tNFC_STATUS status = check_mutex_state(mutex);
    if (status != NFC_STATUS_OK) {
        return status;
    }
    int ret = pthread_cond_wait(&mutex->cond, &mutex->mutex);
    return ret == 0 ? NFC_STATUS_OK : NFC_STATUS_FAILED;
}

/**
 * @brief 等待事件，直到被唤醒或超时
 *
 * 调用后线程进入阻塞状态，等待其他线程调用osi_mutex_notify或是osi_mutex_notify_all唤醒或超时
 *
 * @note 调用此函数前需要先获取互斥锁
 *
 * @param mutex 互斥锁指针
 * @param timeout 超时时间，单位为毫秒
 * @return 成功返回NFC_STATUS_OK，超时返回NFC_STATUS_TIMEOUT，其他情况返回对应错误码
*/
tNFC_STATUS osi_mutex_wait(tMUTEX *mutex, uint64_t timeout)
{
    tNFC_STATUS status = check_mutex_state(mutex);
    if (status != NFC_STATUS_OK) {
        return status;
    }

    struct timespec absolute_time;

    if (clock_gettime(CLOCK_MONOTONIC, &absolute_time) == -1) {
        NFCLOG_E("[%s] clock_gettime failed", mutex->name);
        return NFC_STATUS_FAILED;
    } else {
        absolute_time.tv_sec += timeout / OSI_MS_PER_SEC;
        uint64_t ns = absolute_time.tv_nsec + ((timeout % OSI_MS_PER_SEC) * OSI_NS_PER_MS);
        if (ns >= OSI_NS_PER_SEC) {
            absolute_time.tv_sec++;
            absolute_time.tv_nsec = ns - OSI_NS_PER_SEC;
        } else {
            absolute_time.tv_nsec = ns;
        }
    }

    int ret = pthread_cond_timedwait(&mutex->cond, &mutex->mutex, &absolute_time);
    if (ret == ETIMEDOUT) {
        NFCLOG_W("[%s] wait timeout", mutex->name);
        return NFC_STATUS_TIMEOUT;
    }
    return ret == 0 ? NFC_STATUS_OK : NFC_STATUS_FAILED;
}

/**
 * @brief 唤醒一个等待线程
 *
 * @note 调用此函数前需要先获取互斥锁
 *
 * @param mutex 互斥锁指针
 * @return 成功返回NFC_STATUS_OK，其他情况返回对应错误码
*/
tNFC_STATUS osi_mutex_notify(tMUTEX *mutex)
{
    tNFC_STATUS status = check_mutex_state(mutex);
    if (status != NFC_STATUS_OK) {
        return status;
    }
    int ret = pthread_cond_signal(&mutex->cond);
    return ret == 0 ? NFC_STATUS_OK : NFC_STATUS_FAILED;
}

/**
 * @brief 唤醒所有等待线程
 *
 * @note 调用此函数前需要先获取互斥锁
 *
 * @param mutex 互斥锁指针
 * @return 成功返回NFC_STATUS_OK，其他情况返回对应错误码
*/
tNFC_STATUS osi_mutex_notify_all(tMUTEX *mutex)
{
    tNFC_STATUS status = check_mutex_state(mutex);
    if (status != NFC_STATUS_OK) {
        return status;
    }
    int ret = pthread_cond_broadcast(&mutex->cond);
    return ret == 0 ? NFC_STATUS_OK : NFC_STATUS_FAILED;
}
